#!/usr/bin/env python
# -*- coding: UTF-8 -*-

###################################################################################
# Outils de lecture de texte dans OOo Writer, OOoDraw et OooImpress
# en utilisant un outil de synthèse vocale.
#
# Copyright (c) 2013 by Marie-Pierre Brungard
#
# GNU General Public Licence (GPL) version 3
#
# This is a free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
# This software is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
# You should have received a copy of the GNU General Public License along with
# this software; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA  02111-1307  USA
###################################################################################


import threading
import tempfile
import os
import string
import sys

try:
	import uno
	import unohelper
except:
	pass
try:
	# subprocess n'existe pas avant python 2.4
	import subprocess
except:
	import commands as subprocess
try:
	from com.sun.star.awt import XActionListener
	from com.sun.star.task import XJobExecutor
	from com.sun.star.awt import XKeyHandler
	
	#from com.sun.star.awt.KeyModifier import MOD1
	#from com.sun.star.awt.KeyModifier import MOD2
	#from com.sun.star.awt.Key import LEFT
	#from com.sun.star.awt.Key import RIGHT
	#from com.sun.star.awt.Key import S
	#from com.sun.star.awt.Key import P
	#from com.sun.star.awt.Key import SPACE
except:
	pass
try:
	# nécessaire pour Python 3
	from functools import reduce
except:
	pass
try:
	from urllib.parse import unquote
except:
	from urllib import unquote
try:
	import winsound
except:
	pass
try:
	import pico
except:
	pass

import nls 
from nls import _

# Thread de synthèse/lecture
picothread = None

# information sur la lecture en cours
__modePhrase__ = 1
__modeParagraphe__ = 2
__modeMot__ = 3
__modeSelection__ = 0

modeLecture = __modeParagraphe__ # mode de lecture par défaut

# sens de parcours
__lectureSuivant__ = 1
__lecturePrecedent__ = 2

# autorise raccourcis clavier
__enable_shortcuts_modification__ = False


###################################################################################
# Fonctions utilitaires
###################################################################################

"""
	passage éventuel en unicode (sauf pour Python 3)
"""
def __u__(txt):
	try:
		return unicode(txt, 'utf-8')
	except:
		return txt

def createUnoService(serviceName, ctx=None):
	if ctx is None:
		ctx = uno.getComponentContext()

	sm = ctx.ServiceManager
	try:
		serv = sm.createInstanceWithContext(serviceName, ctx)
	except:
		serv = sm.createInstance(serviceName)

	return serv

def createUnoStruct(cTypeName):
	"""Create a UNO struct and return it.
	Similar to the function of the same name in OOo Basic. -- Copied from Danny Brewer library
	"""
	sm = uno.getComponentContext().ServiceManager
	oCoreReflection = sm.createInstance( "com.sun.star.reflection.CoreReflection" )
	# Get the IDL class for the type name
	oXIdlClass = oCoreReflection.forName( cTypeName )
	# Create the struct.
	oReturnValue, oStruct = oXIdlClass.createObject( None )
	return oStruct

def getBasePath():
	filename = ''

	try:
		pip = uno.getComponentContext().getValueByName("/singletons/com.sun.star.deployment.PackageInformationProvider")
		filename = pip.getPackageLocation("PicoSvoxOOo")
	except:
		pass

	if len(filename) == 0:			
		try:
			filename = os.path.dirname(os.path.abspath(globals()['__file__']))
		except:
			return ""
			
	try:
		i = filename.index('file:')
		filename = filename[i+len('file:'):]
		i = 0
		while filename[i] == os.sep or filename[i] == '/':
			i +=  1
		filename = filename[i:]
	except:
		pass
	
	filename = unquote(filename)
	if filename.endswith('pythonpath'):
		return filename
	return os.sep.join([filename, 'pythonpath'])

"""
	calcule l'intersection entre 2 formes
"""
def intersection(shape1, shape2):

	if shape1 is None or shape2 is None:
		return 0

	x1min = shape1.Position.X
	x1max = x1min + shape1.Size.Width
	y1min = shape1.Position.Y
	y1max = y1min + shape1.Size.Height
		
	x2min = shape2.Position.X
	x2max = x2min + shape2.Size.Width
	y2min = shape2.Position.Y
	y2max = y2min + shape2.Size.Height

	if x1max < x2min or x2max < x1min:
		return 0
	if y1max < y2min or y2max < y1min:
		return 0
	
	# recherche des limites d'intersection
	ximin = max(x1min, x2min)
	ximax = min(x1max, x2max)
	yimin = max(y1min, y2min)
	yimax = min(y1max, y2max)

	return 1.0*(ximax-ximin)*(yimax-yimin)

"""
	retrouve une forme de la couche "layout" a partir de la position d'une cible
"""
def findObjectsByPosition(oDrawPage, oDrawTarget, service = None):
	shapes = []
	if oDrawTarget is None:
		return shapes

	targetSurf = 1.0*oDrawTarget.Size.Width*oDrawTarget.Size.Height
	nNumShapes = oDrawPage.getCount()
	for x in range(nNumShapes):
		oShape = oDrawPage.getByIndex(x)
		if oShape.MoveProtect == False and oShape != oDrawTarget:
			intersectSurf = intersection(oDrawTarget, oShape)
			if intersectSurf/targetSurf > 0:
				if service is None:
					shapes.append(oShape)
				else:
					if oShape.supportsService(service):
						shapes.append(oShape)
	return shapes

"""
	retrouve une forme d'après son nom
"""
def findShapeByName(oShapes, cShapeName):
	"""Find a named shape within an XShapes interface.
	oShapes can be a drawing page, which supports the XShapes interface.
	Thus, you can find a named shape within a draw page, or within a grouped shape,
	or within a selection of sseveral shapes.
	"""
	nNumShapes = oShapes.getCount()
	for i in range(nNumShapes):
		oShape = oShapes.getByIndex(i)
		if oShape.supportsService('com.sun.star.drawing.ControlShape'):
			# champ de formulaire
			try:
				cTheShapeName = oShape.getControl().getName()
			except:
				return None
		else:
			cTheShapeName = oShape.getName()
		if cTheShapeName.strip() == cShapeName.strip():
			return oShape
	return None

"""
	fonction appelable depuis l'interface pour retrouver une forme particulière
"""
def objet(objName):
	oDrawDoc = XSCRIPTCONTEXT.getDocument()
	oDrawDocCtrl = oDrawDoc.getCurrentController()
	oDrawPage = oDrawDocCtrl.getCurrentPage()
	oShape = findShapeByName(oDrawPage, objName)
	return getObjectValue(oShape)

"""
	cherche à récupérer le texte d'une forme. Dans l'ordre : le champ Titre, le texte contenu et enfin le nom
"""
def getObjectValue(oShape):
	if oShape is None:
		return None

	res = ""
	if oShape.supportsService('com.sun.star.drawing.ControlShape'):
		# champ de formulaire
		try:
			res = oShape.getControl().Text
		except:
			pass
	else:
		# forme normale
		res = ""
		# première possibilité : la forme contient du texte
		try:
			res = oShape.getText().getString()
		except:
			pass
		if len(res) == 0:
			# deuxième possibilité : la forme a un champ Titre renseigné
			try:
				res = oShape.Title.strip()
			except:
				pass

	# dernière étape : évaluation éventuelle de la valeur
	valres = __u__(res)
	try:
		return eval(valres)
	except:
		pass

	return valres

"""
	
"""
__temp_wav_file__ = None
def getTempWaveFileName():
	global __temp_wav_file__
	if __temp_wav_file__ is None:
		__temp_wav_file__ = tempfile.mkstemp(suffix=".wav", prefix="svoxpico_")[1]
	return __temp_wav_file__

"""
	
"""
class PicoSvoxOOoKeyHandler(unohelper.Base, XKeyHandler):
	def __init__(self, oDoc):
		self.doc = oDoc
		self.registered = False
		
	def register(self):
		if not self.registered:
			self.doc.getCurrentController().addKeyHandler(self)
			self.registered = True

			# enregistrer les préférences
			saveMaskShortcuts(1)
			
	def unregister(self):
		if self.registered:
			self.doc.getCurrentController().removeKeyHandler(self)
			self.registered = False

			# enregistrer les préférences
			saveMaskShortcuts(0)

	def keyPressed(self, event):
		try:
			ctx = uno.getComponentContext()
			desktop = ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', ctx)
			xDocument = desktop.getCurrentComponent()
			if self.doc.supportsService("com.sun.star.text.TextDocument"):
				if event.Modifiers == 4:
					# touche ALT enfoncée
					if event.KeyCode == 522:
						# ALT + K
						__lire__(xDocument, __modePhrase__)
						return True
					elif event.KeyCode == 524:
						# ALT + M
						__lire__(xDocument, __modeParagraphe__)
						return True
					elif event.KeyCode == 518:
						# ALT + G
						__deplacement__(xDocument, __lectureSuivant__)
						return True
					elif event.KeyCode == 515:
						# ALT + D
						__deplacement__(xDocument, __lecturePrecedent__)
						return True
					elif event.KeyCode == 530:
						# ALT + S
						__stop_lecture__(xDocument)
						return True
				elif event.Modifiers == 2:
					if event.KeyCode == 1284:
						# CTRL + SPACE
						__lire__(xDocument, __modeSelection__)
						return True
			else:
				if event.Modifiers == 4 and event.KeyCode == 530:
					# ALT + S
					__stop_lecture__(xDocument)
					return True
				if event.Modifiers == 2 and event.KeyCode == 1284:
					# CTRL + SPACE
					__lire__(xDocument, __modeSelection__)
					return True
			del desktop
		except:
			pass
		return False

	def keyReleased(self, event):
		return False
		
	def disposing(self, event):
		pass

######################################################################################
# Sauvegarde du masque des phonèmes à mettre en évidence dans le fichier .picosvoxooo
######################################################################################
def readAppData():
	"""Lecture des informations dans le fichier .picosvoxooo"""
	appdata = '.picosvoxooo'
	if 'APPDATA' in os.environ:
		appdata = os.environ.get('APPDATA')+os.sep+'.picosvoxooo'
	elif 'HOME' in os.environ:
		appdata = os.environ.get('HOME')+os.sep+'.picosvoxooo'

	# get the configuration data
	adata = {}
	try:
		if os.path.isfile(appdata):
			fappdata = open(appdata)
			line = fappdata.read()
			fappdata.close()
			
			# eval gives the dict
			adata = eval(line)
	except:
		pass
	return adata

######################################################################################
# Sauvegarde du masque des phonèmes à mettre en évidence dans le fichier .picosvoxooo
######################################################################################
def saveAppData(nappdata, dappdata):
	"""Sauvegarde des informations dans le fichier .picosvoxooo"""
	appdata = '.picosvoxooo'
	if 'APPDATA' in os.environ:
		appdata = os.environ.get('APPDATA')+os.sep+'.picosvoxooo'
	elif 'HOME' in os.environ:
		appdata = os.environ.get('HOME')+os.sep+'.picosvoxooo'

	# first read the data
	adata = readAppData()
	try:
		# then introduce the data in the dict
		adata[nappdata] = dappdata
		
		# and now save the whole dict
		f = open(appdata, 'w')
		f.write(str(adata))
		f.close()
	except:
		pass
	return

######################################################################################
# Sauvegarde de l'information d'utilisation des raccourcis clavier
######################################################################################
def saveMaskShortcuts(shc):
	"""Sauvegarde de l'information d'utilisation des raccourcis clavier"""
	saveAppData('__shortcuts__', str(shc))

######################################################################################
# Lecture de l'information d'utilisation des raccourcis clavier
######################################################################################
def handleMaskShortcuts():
	"""Lecture de l'information d'utilisation des raccourcis clavier"""

	# par défaut : 0
	shc = 0

	# read the file content
	adata = readAppData()

	# lecture dans la structure du fichier
	try:
		shc = int(adata['__shortcuts__'])
	except:
		pass

	return shc

######################################################################################
# Sauvegarde de la vitesse de lecture dans le fichier .picosvoxooo
######################################################################################
def saveMaskSpeed(speed):
	"""Sauvegarde de la vitesse de lecture dans le fichier .picosvoxooo"""
	saveAppData('__speed__', str(speed))

######################################################################################
# Lecture de la vitesse de lecture dans le fichier .picosvoxooo
######################################################################################
def handleMaskSpeed():
	"""Lecture de la vitesse de lecture dans le fichier .picosvoxooo"""

	# par défaut : 100
	speed = 100

	# read the file content
	adata = readAppData()

	# lecture dans la structure du fichier
	try:
		speed = int(adata['__speed__'])
	except:
		pass

	return speed

######################################################################################
# Sauvegarde de la langue de lecture dans le fichier .picosvoxooo
######################################################################################
def saveMaskLanguage(lge):
	"""Sauvegarde de la langue de lecture dans le fichier .picosvoxooo"""
	saveAppData('__language__', lge)

######################################################################################
# Lecture de la langue de lecture dans le fichier .picosvoxooo
######################################################################################
def handleMaskLanguage():
	"""Lecture de la langue de lecture dans le fichier .picosvoxooo"""

	# par défaut : fr
	lge = nls.get_locale_code().replace("_", "-");
        lang_list = ['fr-FR', 'en-GB', 'en-US', 'de-DE', 'es-ES', 'it-IT']
        if not lge in lang_list:
                 lge = 'en-GB'

	# read the file content
	adata = readAppData()

	# lecture dans la structure du fichier
	try:
		lge = adata['__language__']
	except:
		pass

	return lge

###################################################################################
# Thread lecteur
###################################################################################
__pico_lge__ = {'fr-FR':'fr', 'it-IT':'it', 'es-ES':'es', 'de-DE':'de', 'en-GB':'en-gb', 'en-US':'en-us'}
class Lecteur(threading.Thread):
	def __init__(self, txt = '', wavfile = "", speed = -1):
		threading.Thread.__init__(self)

		# lecture de la langue et de la vitesse de lecture
		self.langue = handleMaskLanguage()
		self.speed = handleMaskSpeed()

		self.text = txt
		if speed > 0:
			self.speed = speed
		if len(wavfile) == 0:
			self.wav_file = getTempWaveFileName()
		else:
			self.wav_file = wavfile
		self.synthd = None
		self.player = None
		self._stopevent = threading.Event()
	
	def run(self):
		# synthèse
		if os.name == 'posix':
			# construction de la ligne de commande pico2wave
			sTxt = "<speed level=\""+str(self.speed)+"\">"
			sTxt += self.text
			sTxt += "</speed>"
			cmd = ["pico2wave", "-l", self.langue, "-w"]
			cmd.append(self.wav_file)
			cmd.append(sTxt)

			try:
				# appel de pico2wave
				self.synthd = subprocess.Popen(cmd)
			except:
				pass
			while self.synthd.poll() is None and not self._stopevent.isSet():
				self._stopevent.wait(0.1)
			self.synthd = None
		else:
			data1 = '<genfile file="'+self.wav_file+'">'+self.text+'</genfile>'
			langue = __pico_lge__[self.langue]
			base_path = getBasePath()
			self.synthd = pico.SynthDriver(base_path)
			self.synthd.load_resources(*self.synthd.voice_resources[langue])
			self.synthd.rate = int(self.speed/2)
			pico.speechDictHandler.initialize(langue, base_path)
			data1 = pico.speechDictHandler.processText(data1)
			self.synthd.speak_text(self.synthd.build_string(data1))
			del self.synthd
			self.synthd = None
			
		# lecture du fichier généré
		if not self._stopevent.isSet():
			if os.name == 'posix':
				try:
					# appel de aplay en mode non verbeux
					self.player = subprocess.Popen(["aplay", "-q", self.wav_file])
				except:
					pass
				while self.player.poll() is None and not self._stopevent.isSet():
					self._stopevent.wait(0.1)
				self.player = None
			else:
				# Windows (appel bloquant)
				self.player = True
				winsound.PlaySound(self.wav_file, winsound.SND_FILENAME|winsound.SND_ASYNC)
		
	def stop(self):
		self._stopevent.set()
		if not self.synthd is None:
			if os.name == 'posix':
				if self.synthd.poll() is None:
					self.synthd.terminate()
			else:
				self.synthd.stop()

		if not self.player is None:
			if os.name == 'posix':
				self.player.terminate()
			else:
				winsound.PlaySound(None, winsound.SND_ASYNC)
				self.player = None

###################################################################################
# Lit le passage courant sous le curseur
###################################################################################
def __lire__(xDocument, mode):
	"""Lit le passage courant sous le curseur"""
	global modeLecture
	modeLecture = mode
	
	try:
		xTextRange = getXTextRange(xDocument)
		if xTextRange == None:
			return False
			
		if xDocument.supportsService("com.sun.star.text.TextDocument"):
			# récupération du curseur physique
			xViewCursorSupplier = xDocument.getCurrentController()
			xTextViewCursor = xViewCursorSupplier.getViewCursor()

			for xtr in xTextRange:
				if modeLecture == __modeSelection__:
					if len(xtr.getString()) == 0:
						if not xtr.isStartOfWord():
							xtr.gotoStartOfWord(False)
						if not xtr.isEndOfWord():
							xtr.gotoEndOfWord(True)
					modeLecture = __modeMot__
					xTextViewCursor.gotoRange(xtr, True)
				elif modeLecture == __modeParagraphe__:
					if not xtr.isStartOfParagraph():
						xtr.gotoStartOfParagraph(False)
					if not xtr.isEndOfParagraph():
						xtr.gotoEndOfParagraph(True)
					xTextViewCursor.gotoRange(xtr, True)
				elif modeLecture == __modePhrase__:
					if not xtr.isStartOfSentence():
						xtr.gotoStartOfSentence(False)
					if not xtr.isEndOfSentence():
						xtr.gotoEndOfSentence(True)
					xTextViewCursor.gotoRange(xtr, True)

				__lire_texte__(xtr.getString())
		else:
			oDrawDocCtrl = xDocument.getCurrentController()
			oDrawPage = oDrawDocCtrl.getCurrentPage()

			xIndexAccess = oDrawDocCtrl.getSelection()
			try:
				count = xIndexAccess.getCount()
			except:
				return None
			for i in range(count):
				oShape = xIndexAccess.getByIndex(i)
				spc = getObjectValue(oShape)
				if len(spc) == 0:
					lsh = findObjectsByPosition(oDrawPage, oShape)
					if len(lsh) > 0:
						spc = getObjectValue(lsh[0])
					del lsh
				if len(spc) > 0:
					__lire_texte__(spc)

		del xTextRange
	except:
		return False

	return True

###################################################################################
# Déplacement du curseur vers le passage suivant ou le passage précédent
###################################################################################
def __deplacement__(xDocument, deplacement=__lectureSuivant__):
	"""Déplacement du curseur vers le passage suivant ou le passage précédent"""
	global modeLecture
	if modeLecture == __modeSelection__:
		return False

	# récupération du curseur physique
	xViewCursorSupplier = xDocument.getCurrentController()
	xTextViewCursor = xViewCursorSupplier.getViewCursor()

	if deplacement == __lectureSuivant__:
		xTextViewCursor.collapseToEnd()
	else:
		xTextViewCursor.collapseToStart()

	try:
		xTextRange = getXTextRange(xDocument)
		if xTextRange == None:
			return False

		for xtr in xTextRange:
			msgAlerte = ""
			if modeLecture == __modePhrase__:
				if deplacement == __lecturePrecedent__:
					# passage à la phrase précédente
					if not xtr.gotoPreviousSentence(False):
						if not xtr.gotoPreviousParagraph(False):
							msgAlerte = "Début du texte"
				else:
					# passage à la phrase suivante
					if not xtr.gotoNextSentence(False):
						if not xtr.gotoNextParagraph(False):
							msgAlerte = "Fin du texte"
				xtr.gotoEndOfSentence(True)
			elif modeLecture == __modeParagraphe__:
				if deplacement == __lecturePrecedent__:
					# passage au paragraphe précédent
					if not xtr.gotoPreviousParagraph(False):
						msgAlerte = "Début du texte"
				else:
					# passage au paragraphe suivant
					if not xtr.gotoNextParagraph(False):
						msgAlerte = "Fin du texte"
				xtr.gotoEndOfParagraph(True)
			else:
				if deplacement == __lecturePrecedent__:
					# passage au mot précédent
					if not xtr.gotoPreviousWord(False):
						msgAlerte = "Début du texte"
				else:
					# passage au mot suivant
					if not xtr.gotoNextWord(False):
						msgAlerte = "Fin du texte"
				xtr.gotoEndOfWord(True)
				
			# lecture du texte sélectionné
			if len(msgAlerte) > 0:
				__lire_texte__(msgAlerte)
			else:
				# gestion du curseur physique
				xTextViewCursor.gotoRange(xtr, False)
				xTextViewCursor.gotoRange(xtr, True)

				__lire_texte__(xtr.getString())

		del xTextRange
	except:
		return False
	return True

###################################################################################
# Récupère le textRange correspondant au mot sous le curseur ou à la sélection 
###################################################################################
def getXTextRange(xDocument):
	"""Récupère le textRange correspondant au mot sous le curseur ou à la sélection"""

	#the writer controller impl supports the css.view.XSelectionSupplier interface
	xSelectionSupplier = xDocument.getCurrentController()
 
	xIndexAccess = xSelectionSupplier.getSelection()
	try:
		count = xIndexAccess.getCount()
	except:
		return None

	xTextRanges = []
	for i in range(count):
		xTextRange = xIndexAccess.getByIndex(i)
		theString = xTextRange.getString()
		xText = xTextRange.getText() ## get the XText interface
		xTextRanges.append(xText.createTextCursorByRange(xTextRange))

	return xTextRanges

###################################################################################
# Applique pico2wave et joue le fichier son généré
###################################################################################
def __lire_texte__(txt, wfile="", spd=-1):
	global picothread
	
	if len(txt) == 0:
		return True

	# arrêt préalable de la lecture en cours s'il y a
	__stop_lecture__()
	
	# prétraitement préalable : le texte ext lu ligne par ligne
	ltxt = txt.split(os.linesep)
	for i in range(len(ltxt)):
		ltxt[i] = ltxt[i].strip()
		if not ltxt[i].endswith(('.',';','!','?')):
			ltxt[i] += '.'
	ntxt = ' '.join(ltxt)
	del ltxt

	picothread = Lecteur(ntxt, wavfile=wfile, speed=spd)
	picothread.start()

	return True

#########################################################################################################
#########################################################################################################
###									   FONCTIONS D'INTERFACE
#########################################################################################################
#########################################################################################################

###################################################################################
# Lit la phrase courante sous le curseur
###################################################################################
def lire_phrase( args=None ):
	"""Lit la phrase courante sous le curseur"""
	__lire__(XSCRIPTCONTEXT.getDocument(), __modePhrase__)

###################################################################################
# Lit la phrase courante sous le curseur
###################################################################################
def lire_paragraphe( args=None ):
	"""Lit le paragraphe courant sous le curseur"""
	__lire__(XSCRIPTCONTEXT.getDocument(), __modeParagraphe__)

###################################################################################
# Lit le texte sélectionné
###################################################################################
def lire_selection( args=None ):
	"""Lit le texte sélectionné"""
	__lire__(XSCRIPTCONTEXT.getDocument(), __modeSelection__)

###################################################################################
# Arrête la lecture
###################################################################################
def stop_lecture(args=None):
	"""Arrête la lecture"""
	__stop_lecture__(XSCRIPTCONTEXT.getDocument())

def __stop_lecture__(xDocument = None):
	global picothread

	"""Arrête la lecture"""
	if not xDocument is None:
		if xDocument.supportsService("com.sun.star.text.TextDocument"):
			# récupération du curseur physique
			xViewCursorSupplier = xDocument.getCurrentController()
			xTextViewCursor = xViewCursorSupplier.getViewCursor()
			xTextViewCursor.collapseToEnd()
	
	if not picothread is None:
		picothread.stop()

###################################################################################
# En fonction du mode de lecture passe au passage suivant
###################################################################################
def passage_suivant(args=None):
	"""En fonction du mode de lecture passe au passage suivant"""
	__deplacement__(XSCRIPTCONTEXT.getDocument(), __lectureSuivant__)

###################################################################################
# En fonction du mode de lecture passe au passage précédent
###################################################################################
def passage_precedent(args=None):
	"""En fonction du mode de lecture passe au passage précédent"""
	__deplacement__(XSCRIPTCONTEXT.getDocument(), __lecturePrecedent__)

def createRadioButton(dialogModel, px, py, name, index, label, etat, w=44):
	checkBP = dialogModel.createInstance("com.sun.star.awt.UnoControlRadioButtonModel")
	checkBP.PositionX = px
	checkBP.PositionY = py
	checkBP.Width  = w
	checkBP.Height = 10
	checkBP.Name = name
	checkBP.TabIndex = index
	checkBP.State = etat
	checkBP.Label = label
	return checkBP

###################################################################################
# Crée une boite de dialogue pour configurer Pico SVOX
###################################################################################
def creer_dialog_config( args=None ):
	"""Ouvrir une fenêtre de dialogue pour modifier le champ Title d'une ou de plusieurs formes."""
	__creer_dialog_config__(XSCRIPTCONTEXT.getDocument(), XSCRIPTCONTEXT.getComponentContext())

def __creer_dialog_config__(xDocument, xContext):

	"""Ouvrir une fenêtre de dialogue pour configurer Pico SVOX."""	
	global __enable_shortcuts_modification__

	# Les raccourcis clavier ne fonctionnent pas avec OpenOffice sous Linux.
	# Il faut donc les inhiber.
	sConfigProvider = createUnoService("com.sun.star.configuration.ConfigurationProvider")
	args = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
	args.Name = "nodepath"
	args.Value = "/org.openoffice.Setup/Product"
	oConfig = sConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", (args,))
	if oConfig.ooName == 'OpenOffice.org' and os.name == 'posix':
		__enable_shortcuts_modification__ = False

	smgr = xContext.ServiceManager
	
	# lecture de la langue et de la vitesse de lecture
	langue = handleMaskLanguage()
	vitesse = handleMaskSpeed()
	raccourcis = handleMaskShortcuts()

	# create the dialog model and set the properties 
	dialogModel = smgr.createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", xContext)

	dialogModel.PositionX = 100
	dialogModel.PositionY = 50
	dialogModel.Width = 180 
	if __enable_shortcuts_modification__:
		dialogModel.Height = 110
	else:
		dialogModel.Height = 90
	dialogModel.Title = _("Configuration")

	labelLge = dialogModel.createInstance("com.sun.star.awt.UnoControlFixedTextModel")
	labelLge.PositionX = 10
	labelLge.PositionY = 10
	labelLge.Width  = 120
	labelLge.Height = 12
	labelLge.Name = _("labelLge")
	labelLge.TabIndex = 1
	labelLge.Label = _("Langue de lecture :")
	
	rb1 = createRadioButton(dialogModel, 10, labelLge.PositionY+10, 'rb1', 5, 'fr-FR', (langue == 'fr-FR'), w=44)
	rb2 = createRadioButton(dialogModel, (dialogModel.Width-rb1.Width)/2, rb1.PositionY, 'rb2', 5, 'en-GB', (langue == 'en-GB'), w=rb1.Width)
	rb3 = createRadioButton(dialogModel, dialogModel.Width-10-rb1.Width, rb1.PositionY, 'rb3', 5, 'en-US', (langue == 'en-US'), w=rb1.Width)
	rb4 = createRadioButton(dialogModel, rb1.PositionX, rb1.PositionY+10, 'rb4', 5, 'de-DE', (langue == 'de-DE'), w=rb1.Width)
	rb5 = createRadioButton(dialogModel, rb2.PositionX, rb2.PositionY+10, 'rb5', 5, 'es-ES', (langue == 'es-ES'), w=rb1.Width)
	rb6 = createRadioButton(dialogModel, rb3.PositionX, rb3.PositionY+10, 'rb6', 5, 'it-IT', (langue == 'it-IT'), w=rb1.Width)

	labelSpd = dialogModel.createInstance("com.sun.star.awt.UnoControlFixedTextModel")
	labelSpd.PositionX = 10
	labelSpd.PositionY = 50
	labelSpd.Width  = 70
	labelSpd.Height = 12
	labelSpd.Name = "labelSpd"
	labelSpd.TabIndex = 2
	labelSpd.Label = _("Vitesse :")

	speedNF = dialogModel.createInstance("com.sun.star.awt.UnoControlNumericFieldModel")
	speedNF.PositionX = labelSpd.PositionX+labelSpd.Width
	speedNF.PositionY = labelSpd.PositionY
	speedNF.Width  = 50
	speedNF.Height = labelSpd.Height-2
	speedNF.Name = 'speedNF'
	speedNF.TabIndex = 10
	speedNF.Value = vitesse
	speedNF.ValueMin = 0
	speedNF.ValueMax = 200
	speedNF.ValueStep = 10
	speedNF.Spin = True
	speedNF.DecimalAccuracy = 0

	if __enable_shortcuts_modification__:
		checkRac = dialogModel.createInstance("com.sun.star.awt.UnoControlCheckBoxModel")
		checkRac.PositionX = 10
		checkRac.PositionY = labelSpd.PositionY+20
		checkRac.Width  = dialogModel.Width-20
		checkRac.Height = 12
		checkRac.Name = 'checkRac'
		checkRac.TabIndex = 12
		checkRac.State = False
		checkRac.Label = 'Activer les raccourcis clavier'

	# create the button model and set the properties 
	buttonModel = dialogModel.createInstance("com.sun.star.awt.UnoControlButtonModel")
	buttonModel.Width = 50
	buttonModel.Height = 14
	buttonModel.PositionX = dialogModel.Width/2-buttonModel.Width-2
	buttonModel.PositionY  = dialogModel.Height-20
	buttonModel.Name = "myButtonName"
	buttonModel.TabIndex = 0
	buttonModel.Label = _("Valider")
	
	cancelButtonModel = dialogModel.createInstance("com.sun.star.awt.UnoControlButtonModel")
	cancelButtonModel.Width = 50
	cancelButtonModel.Height = 14
	cancelButtonModel.PositionX = dialogModel.Width/2+2
	cancelButtonModel.PositionY  = dialogModel.Height-20
	cancelButtonModel.Name = "cancelButton"
	cancelButtonModel.TabIndex = 0
	cancelButtonModel.Label = _("Annuler")

	# insert the control models into the dialog model 
	dialogModel.insertByName("myButtonName", buttonModel)
	dialogModel.insertByName("cancelButton", cancelButtonModel)
	dialogModel.insertByName("labelLge", labelLge)
	dialogModel.insertByName("labelSpd", labelSpd)
	dialogModel.insertByName("speedNF", speedNF)
	dialogModel.insertByName("rb1", rb1)
	dialogModel.insertByName("rb2", rb2)
	dialogModel.insertByName("rb3", rb3)
	dialogModel.insertByName("rb4", rb4)
	dialogModel.insertByName("rb5", rb5)
	dialogModel.insertByName("rb6", rb6)
	if __enable_shortcuts_modification__:
		dialogModel.insertByName("checkRac", checkRac)

	# create the dialog control and set the model 
	controlContainer = smgr.createInstanceWithContext("com.sun.star.awt.UnoControlDialog", xContext)
	controlContainer.setModel(dialogModel)
	
	# add the action listener
	try:
		if __enable_shortcuts_modification__:
			controlContainer.getControl("myButtonName").addActionListener(ConfigActionListener(xDocument,
															controlContainer,
															controlContainer.getControl("checkRac"),
															controlContainer.getControl("speedNF"),
															controlContainer.getControl("rb1"),
															controlContainer.getControl("rb2"),
															controlContainer.getControl("rb3"),
															controlContainer.getControl("rb4"),
															controlContainer.getControl("rb5"),
															controlContainer.getControl("rb6")))
		else:
			controlContainer.getControl("myButtonName").addActionListener(ConfigActionListener(None,
															controlContainer,
															None,
															controlContainer.getControl("speedNF"),
															controlContainer.getControl("rb1"),
															controlContainer.getControl("rb2"),
															controlContainer.getControl("rb3"),
															controlContainer.getControl("rb4"),
															controlContainer.getControl("rb5"),
															controlContainer.getControl("rb6")))
	except:
		pass
	controlContainer.getControl("cancelButton").addActionListener(CancelActionListener(controlContainer))

	# create a peer 
	toolkit = smgr.createInstanceWithContext("com.sun.star.awt.ExtToolkit", xContext)

	controlContainer.setVisible(False);	   
	controlContainer.createPeer(toolkit, None);

	# execute it
	controlContainer.execute()

	# dispose the dialog 
	controlContainer.dispose()

###################################################################################
# Déclaration uniquement si le module uno a été chargé
###################################################################################
if 'uno' in sys.modules.keys():	
	######################################################################################
	# Gestionnaire d'événement de la boite de dialogue
	######################################################################################
	class ConfigActionListener(unohelper.Base, XActionListener):
		picosvoxoooKeyHandler = None
		
		def __init__(self, xDocument, controlContainer, checkRac, speedNF, rb1, rb2, rb3, rb4, rb5, rb6):
			self.controlContainer = controlContainer
			self.checkRac = checkRac
			self.speedNF = speedNF
			self.rb1 = rb1
			self.rb2 = rb2
			self.rb3 = rb3
			self.rb4 = rb4
			self.rb5 = rb5
			self.rb6 = rb6
			if not xDocument is None:
				self.__class__.picosvoxoooKeyHandler = PicoSvoxOOoKeyHandler(xDocument)

		def actionPerformed(self, actionEvent):
			# lecture de la langue et de la vitesse de lecture
			langue = handleMaskLanguage()
			vitesse = handleMaskSpeed()

			if not self.checkRac is None:
				try:
					if self.checkRac.getState():
						self.__class__.picosvoxoooKeyHandler.register()
					else:
						self.__class__.picosvoxoooKeyHandler.unregister()
				except:
					pass

			vitesse = self.speedNF.getValue()
			if self.rb1.getState():
				langue = 'fr-FR'
			if self.rb2.getState():
				langue = 'en-GB'
			if self.rb3.getState():
				langue = 'en-US'
			if self.rb4.getState():
				langue = 'de-DE'
			if self.rb5.getState():
				langue = 'es-ES'
			if self.rb6.getState():
				langue = 'it-IT'

			saveMaskLanguage(langue)
			saveMaskSpeed(int(vitesse))

			self.controlContainer.endExecute()

	class CancelActionListener(unohelper.Base, XActionListener):
		def __init__(self, controlContainer):
			self.controlContainer = controlContainer

		def actionPerformed(self, actionEvent):
			self.controlContainer.endExecute()

	###################################################################################
	# lists the scripts, that shall be visible inside OOo. Can be omitted.
	###################################################################################
	g_exportedScripts = lire_phrase, lire_paragraphe, lire_selection, stop_lecture, \
	passage_suivant, passage_precedent, creer_dialog_config,

	class LirePhrase(unohelper.Base, XJobExecutor):
		"""Lit la phrase courante sous le curseur"""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__lire__(desktop.getCurrentComponent(), __modePhrase__)

	class LireParagraphe(unohelper.Base, XJobExecutor):
		"""Lit le paragraphe courant sous le curseur"""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__lire__(desktop.getCurrentComponent(), __modeParagraphe__)

	class LireSelection(unohelper.Base, XJobExecutor):
		"""Lit le texte sélectionné"""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__lire__(desktop.getCurrentComponent(), __modeSelection__)

	class StopLecture(unohelper.Base, XJobExecutor):
		"""Arrête la lecture"""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__stop_lecture__(desktop.getCurrentComponent())

	class PassageSuivant(unohelper.Base, XJobExecutor):
		"""En fonction du mode de lecture passe au passage suivant"""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__deplacement__(desktop.getCurrentComponent(), __lectureSuivant__)

	class PassagePrecedent(unohelper.Base, XJobExecutor):
		"""En fonction du mode de lecture passe au passage précédent"""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__deplacement__(desktop.getCurrentComponent(), __lecturePrecedent__)

	class CreerDialogConfig(unohelper.Base, XJobExecutor):
		"""Ouvrir une fenêtre de dialogue pour configurer Pico SVOX."""
		def __init__(self, ctx):
			self.ctx = ctx
		def trigger(self, args):
			desktop = self.ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.ctx)
			__creer_dialog_config__(desktop.getCurrentComponent(), self.ctx)

	# --- faked component, dummy to allow registration with unopkg, no functionality expected
	g_ImplementationHelper = unohelper.ImplementationHelper()

	g_ImplementationHelper.addImplementation( \
		LirePhrase,'org.picosvoxooo.LirePhrase', \
		('com.sun.star.task.Job',))

	g_ImplementationHelper.addImplementation( \
		LireParagraphe,'org.picosvoxooo.LireParagraphe', \
		('com.sun.star.task.Job',))

	g_ImplementationHelper.addImplementation( \
		LireSelection,'org.picosvoxooo.LireSelection', \
		('com.sun.star.task.Job',))

	g_ImplementationHelper.addImplementation( \
		StopLecture,'org.picosvoxooo.StopLecture', \
		('com.sun.star.task.Job',))

	g_ImplementationHelper.addImplementation( \
		PassageSuivant,'org.picosvoxooo.PassageSuivant', \
		('com.sun.star.task.Job',))

	g_ImplementationHelper.addImplementation( \
		PassagePrecedent,'org.picosvoxooo.PassagePrecedent', \
		('com.sun.star.task.Job',))

	g_ImplementationHelper.addImplementation( \
		CreerDialogConfig,'org.picosvoxooo.CreerDialogConfig', \
		('com.sun.star.task.Job',))

if __name__ == "__main__":
	import time

	data1 = 'Un. Il était une fois.\
	Deux. Il était deux fois.\
	Trois. Il était trois fois.\
	Quatre. Il était quatre fois.\
	Cinq. Il était cinq fois.\
	Six. Il était six fois.\
	Sept. Il était sept fois.\
	Huit. Il était huit fois.\
	Neuf. Il était neuf fois.\
	Dix. Il était dix fois.\
	'
	__lire_texte__(data1, wfile="temp.wav", spd=100)
	time.sleep(6.0)
	__stop_lecture__()
